// 文件：TI的HDC1080传感器驱动程序
// 作者：voyagerhui
// 时间：20210509
// 说明：
// 本程序基于i2c，请先安装i2c-tools，确保设备驱动/dev/i2c-1存在
// 参考switchdoclib的python3驱动程序
// https://github.com/switchdoclabs/SDL_Pi_HDC1080_Python3

#include <stdio.h>  
#include <stdlib.h>
#include <stdbool.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>


// Registers
#define HDC1080_TEMPERATURE_REGISTER    (0x00)
#define HDC1080_HUMIDITY_REGISTER       (0x01)
#define HDC1080_CONFIGURATION_REGISTER  (0x02)
#define HDC1080_MANUFACTURERID_REGISTER (0xFE)
#define HDC1080_DEVICEID_REGISTER       (0xFF)
#define HDC1080_SERIALIDHIGH_REGISTER   (0xFB)
#define HDC1080_SERIALIDMID_REGISTER    (0xFC)
#define HDC1080_SERIALIDBOTTOM_REGISTER (0xFD)

// Configuration Register Bits
#define HDC1080_CONFIG_RESET_BIT                    (0x8000)
#define HDC1080_CONFIG_HEATER_ENABLE                (0x2000)
#define HDC1080_CONFIG_ACQUISITION_MODE             (0x1000)
#define HDC1080_CONFIG_BATTERY_STATUS               (0x0800)
#define HDC1080_CONFIG_TEMPERATURE_RESOLUTION       (0x0400)
#define HDC1080_CONFIG_HUMIDITY_RESOLUTION_HBIT     (0x0200)
#define HDC1080_CONFIG_HUMIDITY_RESOLUTION_LBIT     (0x0100)
#define HDC1080_CONFIG_TEMPERATURE_RESOLUTION_14BIT (0x0000)
#define HDC1080_CONFIG_TEMPERATURE_RESOLUTION_11BIT (0x0400)
#define HDC1080_CONFIG_HUMIDITY_RESOLUTION_14BIT    (0x0000)
#define HDC1080_CONFIG_HUMIDITY_RESOLUTION_11BIT    (0x0100)
#define HDC1080_CONFIG_HUMIDITY_RESOLUTION_8BIT     (0x0200)

// I2C Address
#define HDC1080_ADDRESS (0x40)
#define I2C_SLAVE       (0x0703)

char *i2c_filename = "/dev/i2c-1";
int file = 0;	// i2c device


void hdc1080_init(){
    if((file = open(i2c_filename, O_RDWR)) < 0){
        puts("open i2c failed.");
        exit(1);
    }
    if(ioctl(file, I2C_SLAVE, HDC1080_ADDRESS) < 0){
        puts("Failed to access bus.");  
        exit(1);  
    }
    usleep(15000);

    char wbuf[3] = {HDC1080_CONFIGURATION_REGISTER,
                    HDC1080_CONFIG_ACQUISITION_MODE>>8,
                    0x00};
    write(file, wbuf, 3);
    usleep(15000);
}

double hdc1080_readTemperature(){
    char wbuf[1] = {HDC1080_TEMPERATURE_REGISTER};
    write(file, wbuf, 1);
    usleep(625000);  // From the data sheet

    char rbuf[2];
    read(file, rbuf, 2);
    // Convert the data
    int temp = (rbuf[0] * 256) + rbuf[1];
    double cTemp = (temp / 65536.0) * 165.0 - 40;
    return cTemp;
}

double hdc1080_readHumidity(){
    // Send humidity measurement command, 0x01(01)
    usleep(15000);      // from the datasheet

    char wbuf[1] = {HDC1080_HUMIDITY_REGISTER};
    write(file, wbuf, 1);
    usleep(62500);      // from the datasheet

    char rbuf[2];
    read(file, rbuf, 2);
    // Convert the data
    int humidity = (rbuf[0] * 256) + rbuf[1];
    double chumidity = (humidity / 65536.0) * 100.0;
    return chumidity;
}

int hdc1080_readConfigRegister(){
    // Read config register
    char wbuf[1] = {HDC1080_CONFIGURATION_REGISTER};
    write(file, wbuf, 1);
    usleep(62500);      // from the datasheet

    char rbuf[2];
    read(file, rbuf, 2);
    return (rbuf[0] * 256) + rbuf[1];
}

void hdc1080_turnHeaterOn(){
    int config = hdc1080_readConfigRegister();
    config = config | HDC1080_CONFIG_HEATER_ENABLE;
    
    char wbuf[3] = {HDC1080_CONFIGURATION_REGISTER,
                    config>>8,
                    0x00};
    write(file, wbuf, 3);
    usleep(1500);      // from the datasheet
}

void hdc1080_turnHeaterOff(){
    int config = hdc1080_readConfigRegister();
    config = config | ~HDC1080_CONFIG_HEATER_ENABLE;
    
    char wbuf[3] = {HDC1080_CONFIGURATION_REGISTER,
                    config>>8,
                    0x00};
    write(file, wbuf, 3);
    usleep(1500);      // from the datasheet
}

void hdc1080_setHumidityResolution(int resolution){
    int config = hdc1080_readConfigRegister();
    config = (config & ~0x0300) | resolution;
    
    char wbuf[3] = {HDC1080_CONFIGURATION_REGISTER,
                    config>>8,
                    0x00};
    write(file, wbuf, 3);
    usleep(1500);      // from the datasheet
}

void hdc1080_setTemperatureResolution(int resolution){
    int config = hdc1080_readConfigRegister();
    config = (config & ~0x0400) | resolution;
    
    char wbuf[3] = {HDC1080_CONFIGURATION_REGISTER,
                    config>>8,
                    0x00};
    write(file, wbuf, 3);
    usleep(1500);      // from the datasheet
}

bool hdc1080_readBatteryStatus(){
    int config = hdc1080_readConfigRegister();
    config = config & (~HDC1080_CONFIG_HEATER_ENABLE);
    
    if(config == 0){
        return true;
    }else{
        return false;
    }
}

int hdc1080_readManufacturerID(){
    char wbuf[1] = {HDC1080_MANUFACTURERID_REGISTER};
    write(file, wbuf, 1);
    usleep(62500);      // from the datasheet

    char rbuf[2];
    read(file, rbuf, 2);
    return (rbuf[0] * 256) + rbuf[1];
}

int hdc1080_readDeviceID(){
    char wbuf[1] = {HDC1080_DEVICEID_REGISTER};
    write(file, wbuf, 1);
    usleep(62500);      // from the datasheet

    char rbuf[2];
    read(file, rbuf, 2);
    return (rbuf[0] * 256) + rbuf[1];
}

int hdc1080_readSerialNumber(){
    int serialNumber = 0;
    
    char wbuf[1] = {HDC1080_SERIALIDHIGH_REGISTER};
    write(file, wbuf, 1);
    usleep(62500);      // from the datasheet
    char rbuf[2];
    read(file, rbuf, 2);
    serialNumber = rbuf[0] * 256 + rbuf[1];

    wbuf[0] = HDC1080_SERIALIDMID_REGISTER;
    write(file, wbuf, 1);
    usleep(62500);      // from the datasheet
    read(file, rbuf, 2);
    serialNumber = serialNumber*256 + rbuf[0]*256 + rbuf[1];

    wbuf[0] = HDC1080_SERIALIDBOTTOM_REGISTER;
    write(file, wbuf, 1);
    usleep(62500);      // from the datasheet
    read(file, rbuf, 2);
    serialNumber = serialNumber*256 + rbuf[0]*256 + rbuf[1];
    return serialNumber;
}


int main(){
	hdc1080_init();
    printf("------------");
    printf("Manfacturer ID=0x%X\n", hdc1080_readManufacturerID());
    printf("Device ID=0x%X\n", hdc1080_readDeviceID() );
    printf("Serial Number ID=0x%X\n", hdc1080_readSerialNumber());  
    // read configuration register
    printf("configure register = 0x%X\n", hdc1080_readConfigRegister());
    // turn heater on
    printf("turning Heater On\n");
    hdc1080_turnHeaterOn();
    // read configuration register
    printf("configure register = 0x%X\n", hdc1080_readConfigRegister());
    // turn heater off
    printf("turning Heater Off\n");
    hdc1080_turnHeaterOff();
    // read configuration register
    printf("configure register = 0x%X\n", hdc1080_readConfigRegister());


    // change temperature resolution
    printf("change temperature resolution\n");
    hdc1080_setTemperatureResolution(HDC1080_CONFIG_TEMPERATURE_RESOLUTION_11BIT);
    // read configuration register
    printf("configure register = 0x%X\n", hdc1080_readConfigRegister());
    // change temperature resolution
    printf("change temperature resolution\n");
    hdc1080_setTemperatureResolution(HDC1080_CONFIG_TEMPERATURE_RESOLUTION_14BIT);
    // read configuration register
    printf("configure register = 0x%X\n", hdc1080_readConfigRegister());

    // change humdity resolution
    printf("change humidity resolution\n");
    hdc1080_setHumidityResolution(HDC1080_CONFIG_HUMIDITY_RESOLUTION_8BIT);
    // read configuration register
    printf("configure register = 0x%X\n", hdc1080_readConfigRegister());
    // change humdity resolution
    printf("change humidity resolution\n");
    hdc1080_setHumidityResolution(HDC1080_CONFIG_HUMIDITY_RESOLUTION_14BIT);
    // read configuration register
    printf("configure register = 0x%X\n", hdc1080_readConfigRegister());

    while(true){
        printf("-----------------\n");
        printf("Temperature = %3.1f C\n", hdc1080_readTemperature());
        printf("Humidity = %3.1f %%\n", hdc1080_readHumidity());
        printf("-----------------\n");
        sleep(3);
    }
    return 0;
}
